library(tidyverse)
Sys.setenv(R_CONFIG_ACTIVE = Sys.info()["nodename"])
print(Sys.info()["nodename"])
                       nodename 
"Benjamins-MacBook-Pro-2.local" 
dropbbox_dir <- config::get("dev_analysis_data_dir")
time_points <- readr::read_csv(paste0(dropbbox_dir,"/SST_roi_by_time_point.csv"))
New names:Rows: 425081 Columns: 17── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (3): subid, go_no_go_condition_label, condition
dbl (14): ...1, tr, offset, harvardoxford-cortical_prob_Frontal Orbital Cortex, harvardoxford-cortical_prob_Cingulate Gyrus, anterior division, harvardoxford-subcortical_prob_Left Caudate, h...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Now let’s see if we can graph putamen…

library(ggplot2)
# ggplot(time_points,
#        aes(x=offset, y=`harvardoxford-subcortical_prob_Left Putamen`))+
#   geom_point()+
#   geom_smooth(method="loess",span=2,na.rm=TRUE) + #https://ggplot2.tidyverse.org/reference/geom_smooth.html
#   scale_y_continuous(limits=c(-2.5,2.5))+
#   facet_wrap(~condition)
  

we could also subtract the average per TRIAL. Best we subtract the average across all of the offsets. it’s a bit arbitrary to use the average over all the areas; the idea is just to get a consistent amount of average.

roi_cols <- colnames(time_points)[grepl("harvardoxford",colnames(time_points))]
time_points$tr_roi_mean <- rowMeans(time_points[,roi_cols])
time_points<- 
  time_points %>% group_by(subid,wave) %>%
  mutate(run_mean_across_rois = mean(tr_roi_mean, na.rm = TRUE))

#now mean-center the ROIs
time_points_c<-time_points
for (roi_col in roi_cols){
  time_points_c[,roi_col]<-time_points_c[,roi_col]-time_points_c$run_mean_across_rois
}
sd(time_points$`harvardoxford-subcortical_prob_Left Putamen`)
[1] 0.4566477
sd(time_points_c$`harvardoxford-subcortical_prob_Left Putamen`)
[1] 0.4565868
library(ggplot2)
ggplot(time_points_c,
       aes(x=offset, y=`harvardoxford-subcortical_prob_Left Putamen`,group=interaction(trial_n,subid,wave)))+
  geom_line(alpha=0.2)+
  #geom_point()+
  #geom_smooth(method="loess",span=2,na.rm=TRUE) + #https://ggplot2.tidyverse.org/reference/geom_smooth.html
  scale_y_continuous(limits=c(-2.5,2.5))+
  facet_wrap(~condition)

NA

Now let’s try just graphing the moving average…

library(tidyverse)

#get the range of offsets
min_offset<-round(min(time_points_c$offset))
max_offset <- round(max(time_points_c$offset))

seq_size <- 0.1
offset_size<-1
offset_times <- seq(min_offset,max_offset-max(offset_size,seq_size),seq_size)

dt_list<-list()
for (cond in unique(time_points_c$condition)){
  print(cond)
  for (ot_i in offset_times){
  #<-offset_times[[20]]

  tp_at_offset <- time_points_c %>% filter(offset>=ot_i & offset<(ot_i+offset_size) & condition==cond)
  numeric_cols <- colnames(tp_at_offset)[sapply(tp_at_offset,class)=="numeric"]
  tp_at_offset_m <- data.frame(t(colMeans(tp_at_offset[,numeric_cols],na.rm=TRUE)))
  tp_at_offset_m$offset_start<-ot_i
  tp_at_offset_m$condition<-cond
  dt_list<-append(dt_list,list(tp_at_offset_m))
  if((which(ot_i==offset_times)%%20)==0){
    cat(". ")
  }
  

  }
  print("")

}
[1] "FailedStop"
. . . . . . . . . . . . . . [1] ""
[1] "CorrectGo"
. . . . . . . . . . . . . . [1] ""
[1] "CorrectStop"
. . . . . . . . . . . . . . [1] ""
[1] "FailedGo"
. . . . . . . . . . . . . . [1] ""
df_offset<-data.frame(data.table::rbindlist(dt_list))

get the delay to next trial


behavioral_data_w_tone_data <- readr::read_csv(paste0(dropbbox_dir,"/sst_behavioral_data_all_w_class_type_reveal_onset.csv"))
New names:Rows: 259840 Columns: 26── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr   (3): subid, go_no_go_condition_label, condition
dbl  (22): ...1, trial_n, numchunks_reciprocal, go_no_go_condition, arrow_presented, ladder_number, LadderX_SSD_ms, subject_response, ladder_movement, reaction_time, SSD_technical, SSD_obser...
dttm  (1): rundatetime
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# I want to know: what is the time delay from the tone sound to the PERIOD of the next trial?
#so I want to match each trial up with the _next_ trial
#grab the set of next trials-we want their onset and their duration
#then match that back onto n+1 of their prior trial
trial_df <- behavioral_data_w_tone_data %>% filter(condition!="Cue") %>% 
  arrange(trial_n) %>%
  group_by(subid,waveid,runid) %>%
  mutate(next_trial_n = lead(trial_n,1)) %>%
  ungroup()


next_trial_df <- behavioral_data_w_tone_data %>% 
  filter(condition!="Cue") %>%
  select(trial_n,condition,onset,trial_duration,subid,waveid,runid)


trials_with_next<-merge(
  trial_df,next_trial_df,
  by.x = c("subid","waveid","runid", "next_trial_n"),
  by.y=c("subid","waveid","runid","trial_n"),
  suffixes = c("","_next"))

now, mark for each trial the start and end time point of the next trial, and get measures of how long after each trial the next one appears

trials_with_next$trial_end_next <- trials_with_next$onset_next+trials_with_next$trial_duration_next
trials_with_next$class_type_reveal_onset_to_next_trial_start<-trials_with_next$onset_next-trials_with_next$class_type_reveal_onset
trials_with_next$class_type_reveal_onset_to_next_trial_end<-trials_with_next$trial_end_next-trials_with_next$class_type_reveal_onset
hist(trials_with_next$class_type_reveal_onset_to_next_trial_start,breaks = 100)

median(trials_with_next$class_type_reveal_onset_to_next_trial_start)
[1] 2.797249
hist(trials_with_next$class_type_reveal_onset_to_next_trial_end,breaks = 100)

median(trials_with_next$class_type_reveal_onset_to_next_trial_end)
[1] 4.808165
hist(trials_with_next$class_type_reveal_onset_to_next_trial_start)

next_trial_median <- data.frame(
  "next_trial_start_median" = median(trials_with_next$class_type_reveal_onset_to_next_trial_start),
  "next_trial_end_median" = median(trials_with_next$class_type_reveal_onset_to_next_trial_end)
  )

quantiles <- (1:10)/10
# next_trial_gradient <- data.frame(
#   "quantiles" = quantiles,
#   "next_trial_start" = quantile(trials_with_next$class_type_reveal_onset_to_next_trial_start,(1:10)/10),
#   "next_trial_end" = quantile(trials_with_next$class_type_reveal_onset_to_next_trial_end,(1:10)/10)
#   )

gradient_range = seq(floor(min(trials_with_next$class_type_reveal_onset_to_next_trial_start)),ceiling(max(trials_with_next$class_type_reveal_onset_to_next_trial_end)),0.1)

in_range_by_gradient <- sapply(gradient_range,function(tp){
  time_point_is_after_start <- tp > trials_with_next$class_type_reveal_onset_to_next_trial_start
  time_point_is_before_end <- tp < trials_with_next$class_type_reveal_onset_to_next_trial_end
  number_of_trials <- length(trials_with_next$class_type_reveal_onset_to_next_trial_start) 
  return(sum(time_point_is_after_start & time_point_is_before_end)/number_of_trials)
})

next_trial_gradient <- data.frame("tp"=gradient_range,"pct_in_range"=in_range_by_gradient)

do graphs

now by ROI with facets for condition:

# roi_cols <- colnames(df_offset)[grepl("harvardoxford",colnames(df_offset))]
df_offset_long<-df_offset %>% pivot_longer(cols=roi_cols,names_to="ROI",values_to="value")

unique_rois<-sort(unique(df_offset_long$ROI))
unique_rois_ordered <- unique_rois[order(unique_rois %>% str_extract("(?<=.{3,5}\\.)([A-Za-z]*)$"))]

df_offset_long$ROI<-factor(df_offset_long$ROI,levels = unique_rois_ordered, ordered=TRUE)


ggplot(df_offset_long,
       aes(x=offset_start, y=value,color=ROI))+
  facet_wrap(~condition)+
  #geom_tile(data = next_trial_gradient, aes(x=hrf_tp,alpha=pct_in_range,y=0),fill="#77ff77",color="transparent")+
  annotate("text",x=10,y=0.22,label="Subsequent Go Trial\nExpected HRF",size=2)+
  geom_line(size=1,alpha=0.5)+
  
  scale_color_viridis_d(option = "C")+
  coord_cartesian(ylim=c(-0.1,0.25))+
  ggplot_time_points_extras

  
  #geom_point()+
  #geom_smooth(method="loess",span=2,na.rm=TRUE) + #https://ggplot2.tidyverse.org/reference/geom_smooth.html
  #scale_y_continuous(limits=c(-2.5,2.5))
  

CS-FS

Let’s now try to contrast these time series to see what the CS-FS looks like. This will be interesting because it’s similar to the simple GLM result I got earlier that set me down the path of trying to analyze this.

trial_tps_compare_conditions<-df_offset_long %>% pivot_wider(id_cols=c(ROI,offset_start),names_from = "condition",values_from="value")
trial_tps_compare_conditions$CS_minus_FS<-trial_tps_compare_conditions$CorrectStop-trial_tps_compare_conditions$FailedStop
next_trial_gradient$hrf_tp<-next_trial_gradient$tp+5
ggplot(trial_tps_compare_conditions,
       aes(x=offset_start, y=CS_minus_FS,color=ROI))+
  #geom_tile(data = next_trial_gradient, aes(x=tp,alpha=pct_in_range,y=0),fill="#000000",color="transparent")+
  geom_tile(data = next_trial_gradient, aes(x=hrf_tp,alpha=pct_in_range,y=0),fill="#77ff77",color="transparent")+
  #annotate("text",x=4,y=0.25,label="Subsequent Go Trial\nTime Range",size=2)+
  annotate("text",x=10,y=0.22,label="Subsequent Go Trial\nExpected HRF",size=2)+
  scale_alpha_continuous(name="Subsequent go trial range", range=c(0,0.7),labels=scales::percent_format())+
  geom_line(size=1,alpha=0.5)+
  scale_color_viridis_d(option = "C")+
  ggplot_time_points_extras+
  coord_cartesian(ylim=c(-0.1,0.25))+
  labs(y="CS-FS BOLD activity difference")

NA

Contrast: - FS and CS - Caudate and Putamen - Individual differences in behavioral responsivity to Failed vs correct (in terms of speed-ups/slow-downs), and if this is linked to individual differences in caudate activity - or equally–we could do this at a trial level.

find a way to compare activity from the start and end. Is there a sort of habituation effect?

Easy way first. Take just CS or FS, then do one graph per ROI, use color with the order of the task.


roi_cols <- colnames(time_points_c)[grepl("harvardoxford",colnames(time_points_c))]

next_trial_gradient$hrf_tp<-next_trial_gradient$tp+5

time_points_long<-time_points_c %>% pivot_longer(cols=roi_cols,names_to="ROI",values_to="value")

unique_rois<-sort(unique(time_points_long$ROI))
unique_rois_ordered <- unique_rois[order(unique_rois %>% str_extract("(?<=.{3,5}\\.)([A-Za-z]*)$"))]

time_points_long$ROI<-factor(time_points_long$ROI,levels = unique_rois_ordered, ordered=TRUE)


for (c_i in unique(time_points_long$condition)){
  c_plot <- ggplot(time_points_long %>% filter(condition==c_i),
       aes(x=offset, y=value,color=trial_n))+
  facet_wrap(~ROI)+
  geom_tile(data = next_trial_gradient, aes(x=hrf_tp,alpha=pct_in_range,y=0),fill="#77ff77",color="transparent")+
  annotate("text",x=10,y=0.22,label="Subsequent Go Trial\nExpected HRF",size=2)+
  geom_line(size=1,alpha=0.1)+
  #scale_color_viridis_d(option = "C")+
  #coord_cartesian(ylim=c(-0.1,0.25))+
  ggplot_time_points_extras
  print(c_plot)
  
}

That doesn’t work. OK, let’s try putting the activity into buckets of trials (early, mid, late) then graph for those three groups

library(tidyverse)

#get the range of offsets
min_offset<-round(min(time_points_c$offset))
max_offset <- round(max(time_points_c$offset))

seq_size <- 0.1
offset_size<-1
offset_times <- seq(min_offset,max_offset-max(offset_size,seq_size),seq_size)

dt_list<-list()
time_points_c$trial_bucket<-""
time_points_c$trial_bucket[time_points_c$trial_n<=40]<-"early"
time_points_c$trial_bucket[time_points_c$trial_n%in% seq(139-40,139,2)]<-"mid"
time_points_c$trial_bucket[time_points_c$trial_n %in% seq(239-40,239,2)]<-"late"




for (cond in unique(time_points_c$condition)){
  print(cond)
  for (tb_i in unique(time_points_c$trial_bucket)){
      
    for (ot_i in offset_times){
      
      
      tp_at_offset <- time_points_c %>% filter(offset>=ot_i & offset<(ot_i+offset_size) & condition==cond & trial_bucket==tb_i)
      numeric_cols <- colnames(tp_at_offset)[sapply(tp_at_offset,class)=="numeric"]
      tp_at_offset_m <- data.frame(t(colMeans(tp_at_offset[,numeric_cols],na.rm=TRUE)))
      tp_at_offset_m$offset_start<-ot_i
      tp_at_offset_m$condition<-cond
      tp_at_offset_m$trial_bucket<-tb_i
      dt_list<-append(dt_list,list(tp_at_offset_m))
      if((which(ot_i==offset_times)%%20)==0){
        cat(". ")
      }
    }

  }

}
[1] "FailedStop"
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . [1] "CorrectGo"
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . [1] "CorrectStop"
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . [1] "FailedGo"
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
df_offset_run_buckets<-data.frame(data.table::rbindlist(dt_list))
roi_cols <- colnames(df_offset_run_buckets)[grepl("harvardoxford",colnames(df_offset_run_buckets))]
df_offset_long<-df_offset_run_buckets %>% pivot_longer(cols=roi_cols,names_to="ROI",values_to="value")


df_offset_long <- df_offset_long[!is.nan(df_offset_long$tr), ]
df_offset_long$trial_bucket<-factor(df_offset_long$trial_bucket, levels=c("early","mid","late"))
df_offset_long$ROI_label<-df_offset_long$ROI %>% as.character %>% stringr::str_extract_all(pattern = "(?<=harvardoxford\\..{0,3}cortical_prob_)(.*)") %>% unlist
unique_rois<-sort(unique(df_offset_long$ROI_label))
unique_rois_ordered <- unique_rois[order(unique_rois %>% str_extract("(?<=.{3,5}\\.)([A-Za-z]*)$"))]

df_offset_long$ROI_label<-factor(df_offset_long$ROI_label,levels = unique_rois_ordered, ordered=TRUE)


for (c_i in unique(df_offset_long$condition)){
  c_plot <- ggplot(df_offset_long  %>% filter(condition==c_i),#filter(condition==c_i),
         aes(x=offset_start, y=value,color=trial_bucket,group=trial_bucket))+
    facet_wrap(~ROI_label,ncol = 4)+
    #geom_tile(data = next_trial_gradient, aes(x=hrf_tp,alpha=pct_in_range,y=0),fill="#77ff77",color="transparent")+
    annotate("text",x=10,y=0.22,label="Subsequent Go Trial\nExpected HRF",size=2)+
    geom_line(size=1,alpha=0.5)+
    #scale_color_viridis_d(option = "C")+
    #coord_cartesian(ylim=c(-0.1,0.25))+
    ggplot_time_points_extras+
      labs(
      title="Stop Signal Task Activity",
      subtitle=paste0(c_i)
    )
    
    
  #geom_point()+
  #geom_smooth(method="loess",span=2,na.rm=TRUE) + #https://ggplot2.tidyverse.org/reference/geom_smooth.html
  #scale_y_continuous(limits=c(-2.5,2.5))
  print(c_plot)
}

next_trial_gradient$hrf_tp<-next_trial_gradient$tp+5

for (c_i in unique(df_offset_long$condition)){
  c_plot <- ggplot(df_offset_long %>% filter(condition==c_i & trial_bucket!=""),
       aes(x=offset_start, y=value,color=ROI_label))+
  facet_wrap(~trial_bucket,nrow = 1)+
  geom_tile(data = next_trial_gradient, aes(x=hrf_tp,alpha=pct_in_range,y=0),fill="#77ff77",color="transparent",show.legend=FALSE)+
  annotate("text",x=10,y=0.22,label="Subsequent Go Trial\nExpected HRF",size=2)+
  geom_line(size=1,alpha=0.5)+
  scale_color_viridis_d(option = "C")+
  coord_cartesian(ylim=c(-0.5,0.5))+
  ggplot_time_points_extras+
      labs(
      title="Stop Signal Task Activity",
      subtitle=paste0(c_i)
    )+theme(legend.position = "bottom")
  
  print(c_plot)
}

Can we contrast CS and FS in the way we did above?


trial_tps_compare_conditions<-df_offset_long %>% pivot_wider(id_cols=c(ROI_label,offset_start,trial_bucket),names_from = "condition",values_from="value")
trial_tps_compare_conditions$CS_minus_FS<-trial_tps_compare_conditions$CorrectStop-trial_tps_compare_conditions$FailedStop

  c_plot <- ggplot(trial_tps_compare_conditions %>% filter(trial_bucket!=""),
       aes(x=offset_start, y=CS_minus_FS,color=ROI_label))+
  facet_wrap(~trial_bucket,nrow = 1)+
  geom_tile(data = next_trial_gradient, aes(x=hrf_tp,alpha=pct_in_range,y=0),fill="#77ff77",color="transparent",show.legend=FALSE)+
  annotate("text",x=10,y=0.22,label="Subsequent Go Trial\nExpected HRF",size=2)+
  geom_line(size=1,alpha=0.5)+
  scale_color_viridis_d(option = "C")+
  coord_cartesian(ylim=c(-0.5,0.5))+
  ggplot_time_points_extras+
      labs(
      title="Stop Signal Task Activity",
      subtitle=paste0(c_i),
      y="CS-FS BOLD activity difference")+
    theme(legend.position = "bottom")
print(c_plot)


# 
# ggplot(trial_tps_compare_conditions,
#        aes(x=offset_start, y=CS_minus_FS,color=ROI))+
#   #geom_tile(data = next_trial_gradient, aes(x=tp,alpha=pct_in_range,y=0),fill="#000000",color="transparent")+
#   geom_tile(data = next_trial_gradient, aes(x=hrf_tp,alpha=pct_in_range,y=0),fill="#77ff77",color="transparent")+
#   #annotate("text",x=4,y=0.25,label="Subsequent Go Trial\nTime Range",size=2)+
#   annotate("text",x=10,y=0.22,label="Subsequent Go Trial\nExpected HRF",size=2)+
#   scale_alpha_continuous(name="Subsequent go trial range", range=c(0,0.7),labels=scales::percent_format())+
#   geom_line(size=1,alpha=0.5)+
#   scale_color_viridis_d(option = "C")+
#   ggplot_time_points_extras+
#   coord_cartesian(ylim=c(-0.1,0.25))+
#   labs(y="CS-FS BOLD activity difference")
  

There are a different distribution of the number of trials for each condition, but not in a way that clearly could produce the discrepancy I’m observing above.

time_points_c %>% filter(trial_bucket!="") %>% group_by(condition,trial_bucket) %>% summarize(trial_count=n())
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpTeXMuc2V0ZW52KFJfQ09ORklHX0FDVElWRSA9IFN5cy5pbmZvKClbIm5vZGVuYW1lIl0pCnByaW50KFN5cy5pbmZvKClbIm5vZGVuYW1lIl0pCgpgYGAKCmBgYHtyfQpkcm9wYmJveF9kaXIgPC0gY29uZmlnOjpnZXQoImRldl9hbmFseXNpc19kYXRhX2RpciIpCgpgYGAKCgoKYGBge3J9CnRpbWVfcG9pbnRzIDwtIHJlYWRyOjpyZWFkX2NzdihwYXN0ZTAoZHJvcGJib3hfZGlyLCIvU1NUX3JvaV9ieV90aW1lX3BvaW50LmNzdiIpKQpgYGAKCgpOb3cgbGV0J3Mgc2VlIGlmIHdlIGNhbiBncmFwaCBwdXRhbWVuLi4uCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCiMgZ2dwbG90KHRpbWVfcG9pbnRzLAojICAgICAgICBhZXMoeD1vZmZzZXQsIHk9YGhhcnZhcmRveGZvcmQtc3ViY29ydGljYWxfcHJvYl9MZWZ0IFB1dGFtZW5gKSkrCiMgICBnZW9tX3BvaW50KCkrCiMgICBnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIixzcGFuPTIsbmEucm09VFJVRSkgKyAjaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dlb21fc21vb3RoLmh0bWwKIyAgIHNjYWxlX3lfY29udGludW91cyhsaW1pdHM9YygtMi41LDIuNSkpKwojICAgZmFjZXRfd3JhcCh+Y29uZGl0aW9uKQogIApgYGAKCgoKd2UgY291bGQgYWxzbyBzdWJ0cmFjdCB0aGUgYXZlcmFnZSBwZXIgVFJJQUwuIEJlc3Qgd2Ugc3VidHJhY3QgdGhlIGF2ZXJhZ2UgYWNyb3NzIGFsbCBvZiB0aGUgb2Zmc2V0cy4gaXQncyBhIGJpdCBhcmJpdHJhcnkgdG8gdXNlIHRoZSBhdmVyYWdlIG92ZXIgYWxsIHRoZSBhcmVhczsgdGhlIGlkZWEgaXMganVzdCB0byBnZXQgYSBjb25zaXN0ZW50IGFtb3VudCBvZiBhdmVyYWdlLgoKYGBge3J9CnJvaV9jb2xzIDwtIGNvbG5hbWVzKHRpbWVfcG9pbnRzKVtncmVwbCgiaGFydmFyZG94Zm9yZCIsY29sbmFtZXModGltZV9wb2ludHMpKV0KdGltZV9wb2ludHMkdHJfcm9pX21lYW4gPC0gcm93TWVhbnModGltZV9wb2ludHNbLHJvaV9jb2xzXSkKdGltZV9wb2ludHM8LSAKICB0aW1lX3BvaW50cyAlPiUgZ3JvdXBfYnkoc3ViaWQsd2F2ZSkgJT4lCiAgbXV0YXRlKHJ1bl9tZWFuX2Fjcm9zc19yb2lzID0gbWVhbih0cl9yb2lfbWVhbiwgbmEucm0gPSBUUlVFKSkKCiNub3cgbWVhbi1jZW50ZXIgdGhlIFJPSXMKdGltZV9wb2ludHNfYzwtdGltZV9wb2ludHMKZm9yIChyb2lfY29sIGluIHJvaV9jb2xzKXsKICB0aW1lX3BvaW50c19jWyxyb2lfY29sXTwtdGltZV9wb2ludHNfY1sscm9pX2NvbF0tdGltZV9wb2ludHNfYyRydW5fbWVhbl9hY3Jvc3Nfcm9pcwp9CmBgYAoKYGBge3J9CnNkKHRpbWVfcG9pbnRzJGBoYXJ2YXJkb3hmb3JkLXN1YmNvcnRpY2FsX3Byb2JfTGVmdCBQdXRhbWVuYCkKc2QodGltZV9wb2ludHNfYyRgaGFydmFyZG94Zm9yZC1zdWJjb3J0aWNhbF9wcm9iX0xlZnQgUHV0YW1lbmApCmBgYAoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKZ2dwbG90KHRpbWVfcG9pbnRzX2MsCiAgICAgICBhZXMoeD1vZmZzZXQsIHk9YGhhcnZhcmRveGZvcmQtc3ViY29ydGljYWxfcHJvYl9MZWZ0IFB1dGFtZW5gLGdyb3VwPWludGVyYWN0aW9uKHRyaWFsX24sc3ViaWQsd2F2ZSkpKSsKICBnZW9tX2xpbmUoYWxwaGE9MC4yKSsKICAjZ2VvbV9wb2ludCgpKwogICNnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIixzcGFuPTIsbmEucm09VFJVRSkgKyAjaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dlb21fc21vb3RoLmh0bWwKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzPWMoLTIuNSwyLjUpKSsKICBmYWNldF93cmFwKH5jb25kaXRpb24pCiAgCmBgYAoKTm93IGxldCdzIHRyeSBqdXN0IGdyYXBoaW5nIHRoZSBtb3ZpbmcgYXZlcmFnZS4uLgoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQoKI2dldCB0aGUgcmFuZ2Ugb2Ygb2Zmc2V0cwptaW5fb2Zmc2V0PC1yb3VuZChtaW4odGltZV9wb2ludHNfYyRvZmZzZXQpKQptYXhfb2Zmc2V0IDwtIHJvdW5kKG1heCh0aW1lX3BvaW50c19jJG9mZnNldCkpCgpzZXFfc2l6ZSA8LSAwLjEKb2Zmc2V0X3NpemU8LTEKb2Zmc2V0X3RpbWVzIDwtIHNlcShtaW5fb2Zmc2V0LG1heF9vZmZzZXQtbWF4KG9mZnNldF9zaXplLHNlcV9zaXplKSxzZXFfc2l6ZSkKCmR0X2xpc3Q8LWxpc3QoKQpmb3IgKGNvbmQgaW4gdW5pcXVlKHRpbWVfcG9pbnRzX2MkY29uZGl0aW9uKSl7CiAgcHJpbnQoY29uZCkKICBmb3IgKG90X2kgaW4gb2Zmc2V0X3RpbWVzKXsKICAjPC1vZmZzZXRfdGltZXNbWzIwXV0KCiAgdHBfYXRfb2Zmc2V0IDwtIHRpbWVfcG9pbnRzX2MgJT4lIGZpbHRlcihvZmZzZXQ+PW90X2kgJiBvZmZzZXQ8KG90X2krb2Zmc2V0X3NpemUpICYgY29uZGl0aW9uPT1jb25kKQogIG51bWVyaWNfY29scyA8LSBjb2xuYW1lcyh0cF9hdF9vZmZzZXQpW3NhcHBseSh0cF9hdF9vZmZzZXQsY2xhc3MpPT0ibnVtZXJpYyJdCiAgdHBfYXRfb2Zmc2V0X20gPC0gZGF0YS5mcmFtZSh0KGNvbE1lYW5zKHRwX2F0X29mZnNldFssbnVtZXJpY19jb2xzXSxuYS5ybT1UUlVFKSkpCiAgdHBfYXRfb2Zmc2V0X20kb2Zmc2V0X3N0YXJ0PC1vdF9pCiAgdHBfYXRfb2Zmc2V0X20kY29uZGl0aW9uPC1jb25kCiAgZHRfbGlzdDwtYXBwZW5kKGR0X2xpc3QsbGlzdCh0cF9hdF9vZmZzZXRfbSkpCiAgaWYoKHdoaWNoKG90X2k9PW9mZnNldF90aW1lcyklJTIwKT09MCl7CiAgICBjYXQoIi4gIikKICB9CiAgCgogIH0KICBwcmludCgiIikKCn0KCmRmX29mZnNldDwtZGF0YS5mcmFtZShkYXRhLnRhYmxlOjpyYmluZGxpc3QoZHRfbGlzdCkpCgoKYGBgCgojIGdldCB0aGUgZGVsYXkgdG8gbmV4dCB0cmlhbAoKYGBge3J9CgpiZWhhdmlvcmFsX2RhdGFfd190b25lX2RhdGEgPC0gcmVhZHI6OnJlYWRfY3N2KHBhc3RlMChkcm9wYmJveF9kaXIsIi9zc3RfYmVoYXZpb3JhbF9kYXRhX2FsbF93X2NsYXNzX3R5cGVfcmV2ZWFsX29uc2V0LmNzdiIpKQoKIyBJIHdhbnQgdG8ga25vdzogd2hhdCBpcyB0aGUgdGltZSBkZWxheSBmcm9tIHRoZSB0b25lIHNvdW5kIHRvIHRoZSBQRVJJT0Qgb2YgdGhlIG5leHQgdHJpYWw/CgpgYGAKCgpgYGB7cn0KI3NvIEkgd2FudCB0byBtYXRjaCBlYWNoIHRyaWFsIHVwIHdpdGggdGhlIF9uZXh0XyB0cmlhbAojZ3JhYiB0aGUgc2V0IG9mIG5leHQgdHJpYWxzLXdlIHdhbnQgdGhlaXIgb25zZXQgYW5kIHRoZWlyIGR1cmF0aW9uCiN0aGVuIG1hdGNoIHRoYXQgYmFjayBvbnRvIG4rMSBvZiB0aGVpciBwcmlvciB0cmlhbAp0cmlhbF9kZiA8LSBiZWhhdmlvcmFsX2RhdGFfd190b25lX2RhdGEgJT4lIGZpbHRlcihjb25kaXRpb24hPSJDdWUiKSAlPiUgCiAgYXJyYW5nZSh0cmlhbF9uKSAlPiUKICBncm91cF9ieShzdWJpZCx3YXZlaWQscnVuaWQpICU+JQogIG11dGF0ZShuZXh0X3RyaWFsX24gPSBsZWFkKHRyaWFsX24sMSkpICU+JQogIHVuZ3JvdXAoKQoKCm5leHRfdHJpYWxfZGYgPC0gYmVoYXZpb3JhbF9kYXRhX3dfdG9uZV9kYXRhICU+JSAKICBmaWx0ZXIoY29uZGl0aW9uIT0iQ3VlIikgJT4lCiAgc2VsZWN0KHRyaWFsX24sY29uZGl0aW9uLG9uc2V0LHRyaWFsX2R1cmF0aW9uLHN1YmlkLHdhdmVpZCxydW5pZCkKCgp0cmlhbHNfd2l0aF9uZXh0PC1tZXJnZSgKICB0cmlhbF9kZixuZXh0X3RyaWFsX2RmLAogIGJ5LnggPSBjKCJzdWJpZCIsIndhdmVpZCIsInJ1bmlkIiwgIm5leHRfdHJpYWxfbiIpLAogIGJ5Lnk9Yygic3ViaWQiLCJ3YXZlaWQiLCJydW5pZCIsInRyaWFsX24iKSwKICBzdWZmaXhlcyA9IGMoIiIsIl9uZXh0IikpCmBgYAoKCm5vdywgbWFyayBmb3IgZWFjaCB0cmlhbCB0aGUgc3RhcnQgYW5kIGVuZCB0aW1lIHBvaW50IG9mIHRoZSBuZXh0IHRyaWFsLCBhbmQgZ2V0IG1lYXN1cmVzIG9mIGhvdyBsb25nIGFmdGVyIGVhY2ggdHJpYWwgdGhlIG5leHQgb25lIGFwcGVhcnMKCmBgYHtyfQp0cmlhbHNfd2l0aF9uZXh0JHRyaWFsX2VuZF9uZXh0IDwtIHRyaWFsc193aXRoX25leHQkb25zZXRfbmV4dCt0cmlhbHNfd2l0aF9uZXh0JHRyaWFsX2R1cmF0aW9uX25leHQKdHJpYWxzX3dpdGhfbmV4dCRjbGFzc190eXBlX3JldmVhbF9vbnNldF90b19uZXh0X3RyaWFsX3N0YXJ0PC10cmlhbHNfd2l0aF9uZXh0JG9uc2V0X25leHQtdHJpYWxzX3dpdGhfbmV4dCRjbGFzc190eXBlX3JldmVhbF9vbnNldAp0cmlhbHNfd2l0aF9uZXh0JGNsYXNzX3R5cGVfcmV2ZWFsX29uc2V0X3RvX25leHRfdHJpYWxfZW5kPC10cmlhbHNfd2l0aF9uZXh0JHRyaWFsX2VuZF9uZXh0LXRyaWFsc193aXRoX25leHQkY2xhc3NfdHlwZV9yZXZlYWxfb25zZXQKYGBgCgpgYGB7cn0KaGlzdCh0cmlhbHNfd2l0aF9uZXh0JGNsYXNzX3R5cGVfcmV2ZWFsX29uc2V0X3RvX25leHRfdHJpYWxfc3RhcnQsYnJlYWtzID0gMTAwKQptZWRpYW4odHJpYWxzX3dpdGhfbmV4dCRjbGFzc190eXBlX3JldmVhbF9vbnNldF90b19uZXh0X3RyaWFsX3N0YXJ0KQpgYGAKCgoKYGBge3J9Cmhpc3QodHJpYWxzX3dpdGhfbmV4dCRjbGFzc190eXBlX3JldmVhbF9vbnNldF90b19uZXh0X3RyaWFsX2VuZCxicmVha3MgPSAxMDApCm1lZGlhbih0cmlhbHNfd2l0aF9uZXh0JGNsYXNzX3R5cGVfcmV2ZWFsX29uc2V0X3RvX25leHRfdHJpYWxfZW5kKQpgYGAKYGBge3J9Cmhpc3QodHJpYWxzX3dpdGhfbmV4dCRjbGFzc190eXBlX3JldmVhbF9vbnNldF90b19uZXh0X3RyaWFsX3N0YXJ0KQpgYGAKCmBgYHtyfQpuZXh0X3RyaWFsX21lZGlhbiA8LSBkYXRhLmZyYW1lKAogICJuZXh0X3RyaWFsX3N0YXJ0X21lZGlhbiIgPSBtZWRpYW4odHJpYWxzX3dpdGhfbmV4dCRjbGFzc190eXBlX3JldmVhbF9vbnNldF90b19uZXh0X3RyaWFsX3N0YXJ0KSwKICAibmV4dF90cmlhbF9lbmRfbWVkaWFuIiA9IG1lZGlhbih0cmlhbHNfd2l0aF9uZXh0JGNsYXNzX3R5cGVfcmV2ZWFsX29uc2V0X3RvX25leHRfdHJpYWxfZW5kKQogICkKCnF1YW50aWxlcyA8LSAoMToxMCkvMTAKIyBuZXh0X3RyaWFsX2dyYWRpZW50IDwtIGRhdGEuZnJhbWUoCiMgICAicXVhbnRpbGVzIiA9IHF1YW50aWxlcywKIyAgICJuZXh0X3RyaWFsX3N0YXJ0IiA9IHF1YW50aWxlKHRyaWFsc193aXRoX25leHQkY2xhc3NfdHlwZV9yZXZlYWxfb25zZXRfdG9fbmV4dF90cmlhbF9zdGFydCwoMToxMCkvMTApLAojICAgIm5leHRfdHJpYWxfZW5kIiA9IHF1YW50aWxlKHRyaWFsc193aXRoX25leHQkY2xhc3NfdHlwZV9yZXZlYWxfb25zZXRfdG9fbmV4dF90cmlhbF9lbmQsKDE6MTApLzEwKQojICAgKQoKZ3JhZGllbnRfcmFuZ2UgPSBzZXEoZmxvb3IobWluKHRyaWFsc193aXRoX25leHQkY2xhc3NfdHlwZV9yZXZlYWxfb25zZXRfdG9fbmV4dF90cmlhbF9zdGFydCkpLGNlaWxpbmcobWF4KHRyaWFsc193aXRoX25leHQkY2xhc3NfdHlwZV9yZXZlYWxfb25zZXRfdG9fbmV4dF90cmlhbF9lbmQpKSwwLjEpCgppbl9yYW5nZV9ieV9ncmFkaWVudCA8LSBzYXBwbHkoZ3JhZGllbnRfcmFuZ2UsZnVuY3Rpb24odHApewogIHRpbWVfcG9pbnRfaXNfYWZ0ZXJfc3RhcnQgPC0gdHAgPiB0cmlhbHNfd2l0aF9uZXh0JGNsYXNzX3R5cGVfcmV2ZWFsX29uc2V0X3RvX25leHRfdHJpYWxfc3RhcnQKICB0aW1lX3BvaW50X2lzX2JlZm9yZV9lbmQgPC0gdHAgPCB0cmlhbHNfd2l0aF9uZXh0JGNsYXNzX3R5cGVfcmV2ZWFsX29uc2V0X3RvX25leHRfdHJpYWxfZW5kCiAgbnVtYmVyX29mX3RyaWFscyA8LSBsZW5ndGgodHJpYWxzX3dpdGhfbmV4dCRjbGFzc190eXBlX3JldmVhbF9vbnNldF90b19uZXh0X3RyaWFsX3N0YXJ0KSAKICByZXR1cm4oc3VtKHRpbWVfcG9pbnRfaXNfYWZ0ZXJfc3RhcnQgJiB0aW1lX3BvaW50X2lzX2JlZm9yZV9lbmQpL251bWJlcl9vZl90cmlhbHMpCn0pCgpuZXh0X3RyaWFsX2dyYWRpZW50IDwtIGRhdGEuZnJhbWUoInRwIj1ncmFkaWVudF9yYW5nZSwicGN0X2luX3JhbmdlIj1pbl9yYW5nZV9ieV9ncmFkaWVudCkKYGBgCgoKCiMgZG8gZ3JhcGhzCgpgYGB7cn0KCgpyb2lfY29scyA8LSBjb2xuYW1lcyhkZl9vZmZzZXQpW2dyZXBsKCJoYXJ2YXJkb3hmb3JkIixjb2xuYW1lcyhkZl9vZmZzZXQpKV0Kc3Vial9jb3VudCA8LSBsZW5ndGgodW5pcXVlKHRpbWVfcG9pbnRzX2Mkc3ViaWQpKQpkZl9vZmZzZXRfbG9uZzwtZGZfb2Zmc2V0ICU+JSBwaXZvdF9sb25nZXIoY29scz1yb2lfY29scyxuYW1lc190bz0iUk9JX2Z1bGxuYW1lIix2YWx1ZXNfdG89InZhbHVlIikKZGZfb2Zmc2V0X2xvbmckUk9JPC1zdHJpbmdyOjpzdHJfZXh0cmFjdF9hbGwoZGZfb2Zmc2V0X2xvbmckUk9JX2Z1bGxuYW1lLHBhdHRlcm4gPSAiKD88PWhhcnZhcmRveGZvcmRcXC4uezAsM31jb3J0aWNhbF9wcm9iXykoLiopIikgJT4lIHN1YigiXFwuIiwiICIsLikKCnVuaXF1ZV9yb2lzPC1zb3J0KHVuaXF1ZShkZl9vZmZzZXRfbG9uZyRST0kpKQp1bmlxdWVfcm9pc19vcmRlcmVkIDwtIHVuaXF1ZV9yb2lzW29yZGVyKHVuaXF1ZV9yb2lzICU+JSBzdHJfZXh0cmFjdCgiKD88PS57Myw1fVxccykoLiopIikpXQoKZGZfb2Zmc2V0X2xvbmckUk9JPC1mYWN0b3IoZGZfb2Zmc2V0X2xvbmckUk9JLGxldmVscyA9IHVuaXF1ZV9yb2lzX29yZGVyZWQsIG9yZGVyZWQ9VFJVRSkKCmdncGxvdF90aW1lX3BvaW50c19leHRyYXMgPC0gbGlzdCgKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLGxpbmV0eXBlPSJkYXNoZWQiKSwKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLGxpbmV0eXBlPSJkb3R0ZWQiKSwKICBsYWJzKAogICAgeD0idGltZSBmcm9tIHRvbmUgc291bmQgb3IgZXhwZWN0ZWQgdGltZSBvZiB0b25lIChzKSIsCiAgICB5PSJtZWFuIEJPTEQgYWN0aXZpdHkgKG5vIEhSRiBjb252b2x1dGlvbikiLAogICAgdGl0bGU9IlN0b3AgU2lnbmFsIFRhc2sgQWN0aXZpdHkiLAogICAgc3VidGl0bGU9cGFzdGUwKCJBY3Jvc3MgIiwgYXMuY2hhcmFjdGVyKHN1YmpfY291bnQpLCIgc3ViamVjdHMgd2l0aCAyNTUgdHJpYWxzIGVhY2giKSwKICAgIGNhcHRpb249IjAgcG9pbnQgaXMgdGhlIG1vbWVudCBvZiB0b25lIHNvdW5kIChTdG9wIHRyaWFscylcbiBvciBleHBlY3RlZCB0b25lIHNvdW5kIGJhc2VkIG9uIHByaW9yIHRyaWFsIChHbyB0cmlhbHMpXG4gSFJGIGNvbnZvbHV0aW9uIG5vdCBhcHBsaWVkXG4xIHMgbW92aW5nIGF2ZXJhZ2VzIikKICApCgoKICAKZ2dwbG90KGRmX29mZnNldF9sb25nLAogICAgICAgYWVzKHg9b2Zmc2V0X3N0YXJ0LCB5PXZhbHVlLGNvbG9yPWNvbmRpdGlvbikpKwogIGdlb21fbGluZSgpKwogIGZhY2V0X3dyYXAoflJPSSxuY29sID0gMikrCiAgZ2dwbG90X3RpbWVfcG9pbnRzX2V4dHJhcwogIAogICNnZW9tX3BvaW50KCkrCiAgI2dlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLHNwYW49MixuYS5ybT1UUlVFKSArICNodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZ2VvbV9zbW9vdGguaHRtbAogICNzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzPWMoLTIuNSwyLjUpKQogIAoKYGBgCgpub3cgYnkgUk9JIHdpdGggZmFjZXRzIGZvciBjb25kaXRpb246CgpgYGB7cn0KIyByb2lfY29scyA8LSBjb2xuYW1lcyhkZl9vZmZzZXQpW2dyZXBsKCJoYXJ2YXJkb3hmb3JkIixjb2xuYW1lcyhkZl9vZmZzZXQpKV0KZGZfb2Zmc2V0X2xvbmc8LWRmX29mZnNldCAlPiUgcGl2b3RfbG9uZ2VyKGNvbHM9cm9pX2NvbHMsbmFtZXNfdG89IlJPSSIsdmFsdWVzX3RvPSJ2YWx1ZSIpCgp1bmlxdWVfcm9pczwtc29ydCh1bmlxdWUoZGZfb2Zmc2V0X2xvbmckUk9JKSkKdW5pcXVlX3JvaXNfb3JkZXJlZCA8LSB1bmlxdWVfcm9pc1tvcmRlcih1bmlxdWVfcm9pcyAlPiUgc3RyX2V4dHJhY3QoIig/PD0uezMsNX1cXC4pKFtBLVphLXpdKikkIikpXQoKZGZfb2Zmc2V0X2xvbmckUk9JPC1mYWN0b3IoZGZfb2Zmc2V0X2xvbmckUk9JLGxldmVscyA9IHVuaXF1ZV9yb2lzX29yZGVyZWQsIG9yZGVyZWQ9VFJVRSkKCgpnZ3Bsb3QoZGZfb2Zmc2V0X2xvbmcsCiAgICAgICBhZXMoeD1vZmZzZXRfc3RhcnQsIHk9dmFsdWUsY29sb3I9Uk9JKSkrCiAgZmFjZXRfd3JhcCh+Y29uZGl0aW9uKSsKICAjZ2VvbV90aWxlKGRhdGEgPSBuZXh0X3RyaWFsX2dyYWRpZW50LCBhZXMoeD1ocmZfdHAsYWxwaGE9cGN0X2luX3JhbmdlLHk9MCksZmlsbD0iIzc3ZmY3NyIsY29sb3I9InRyYW5zcGFyZW50IikrCiAgYW5ub3RhdGUoInRleHQiLHg9MTAseT0wLjIyLGxhYmVsPSJTdWJzZXF1ZW50IEdvIFRyaWFsXG5FeHBlY3RlZCBIUkYiLHNpemU9MikrCiAgZ2VvbV9saW5lKHNpemU9MSxhbHBoYT0wLjUpKwogIAogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZChvcHRpb24gPSAiQyIpKwogIGNvb3JkX2NhcnRlc2lhbih5bGltPWMoLTAuMSwwLjI1KSkrCiAgZ2dwbG90X3RpbWVfcG9pbnRzX2V4dHJhcwogIAogICNnZW9tX3BvaW50KCkrCiAgI2dlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLHNwYW49MixuYS5ybT1UUlVFKSArICNodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZ2VvbV9zbW9vdGguaHRtbAogICNzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzPWMoLTIuNSwyLjUpKQogIAoKYGBgCiMjIENTLUZTCgpMZXQncyBub3cgdHJ5IHRvIGNvbnRyYXN0IHRoZXNlIHRpbWUgc2VyaWVzIHRvIHNlZSB3aGF0IHRoZSBDUy1GUyBsb29rcyBsaWtlLiBUaGlzIHdpbGwgYmUgaW50ZXJlc3RpbmcgYmVjYXVzZSBpdCdzIHNpbWlsYXIgdG8gdGhlIHNpbXBsZSBHTE0gcmVzdWx0IEkgZ290IGVhcmxpZXIgdGhhdCBzZXQgbWUgZG93biB0aGUgcGF0aCBvZiB0cnlpbmcgdG8gYW5hbHl6ZSB0aGlzLgoKYGBge3J9CnRyaWFsX3Rwc19jb21wYXJlX2NvbmRpdGlvbnM8LWRmX29mZnNldF9sb25nICU+JSBwaXZvdF93aWRlcihpZF9jb2xzPWMoUk9JLG9mZnNldF9zdGFydCksbmFtZXNfZnJvbSA9ICJjb25kaXRpb24iLHZhbHVlc19mcm9tPSJ2YWx1ZSIpCnRyaWFsX3Rwc19jb21wYXJlX2NvbmRpdGlvbnMkQ1NfbWludXNfRlM8LXRyaWFsX3Rwc19jb21wYXJlX2NvbmRpdGlvbnMkQ29ycmVjdFN0b3AtdHJpYWxfdHBzX2NvbXBhcmVfY29uZGl0aW9ucyRGYWlsZWRTdG9wCmBgYAoKCgpgYGB7cn0KbmV4dF90cmlhbF9ncmFkaWVudCRocmZfdHA8LW5leHRfdHJpYWxfZ3JhZGllbnQkdHArNQpnZ3Bsb3QodHJpYWxfdHBzX2NvbXBhcmVfY29uZGl0aW9ucywKICAgICAgIGFlcyh4PW9mZnNldF9zdGFydCwgeT1DU19taW51c19GUyxjb2xvcj1ST0kpKSsKICAjZ2VvbV90aWxlKGRhdGEgPSBuZXh0X3RyaWFsX2dyYWRpZW50LCBhZXMoeD10cCxhbHBoYT1wY3RfaW5fcmFuZ2UseT0wKSxmaWxsPSIjMDAwMDAwIixjb2xvcj0idHJhbnNwYXJlbnQiKSsKICBnZW9tX3RpbGUoZGF0YSA9IG5leHRfdHJpYWxfZ3JhZGllbnQsIGFlcyh4PWhyZl90cCxhbHBoYT1wY3RfaW5fcmFuZ2UseT0wKSxmaWxsPSIjNzdmZjc3Iixjb2xvcj0idHJhbnNwYXJlbnQiKSsKICAjYW5ub3RhdGUoInRleHQiLHg9NCx5PTAuMjUsbGFiZWw9IlN1YnNlcXVlbnQgR28gVHJpYWxcblRpbWUgUmFuZ2UiLHNpemU9MikrCiAgYW5ub3RhdGUoInRleHQiLHg9MTAseT0wLjIyLGxhYmVsPSJTdWJzZXF1ZW50IEdvIFRyaWFsXG5FeHBlY3RlZCBIUkYiLHNpemU9MikrCiAgc2NhbGVfYWxwaGFfY29udGludW91cyhuYW1lPSJTdWJzZXF1ZW50IGdvIHRyaWFsIHJhbmdlIiwgcmFuZ2U9YygwLDAuNyksbGFiZWxzPXNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSkrCiAgZ2VvbV9saW5lKHNpemU9MSxhbHBoYT0wLjUpKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZChvcHRpb24gPSAiQyIpKwogIGdncGxvdF90aW1lX3BvaW50c19leHRyYXMrCiAgY29vcmRfY2FydGVzaWFuKHlsaW09YygtMC4xLDAuMjUpKSsKICBsYWJzKHk9IkNTLUZTIEJPTEQgYWN0aXZpdHkgZGlmZmVyZW5jZSIpCiAgCmBgYAoKCgpDb250cmFzdDoKIC0gRlMgYW5kIENTCiAtIENhdWRhdGUgYW5kIFB1dGFtZW4KIC0gSW5kaXZpZHVhbCBkaWZmZXJlbmNlcyBpbiBiZWhhdmlvcmFsIHJlc3BvbnNpdml0eSB0byBGYWlsZWQgdnMgY29ycmVjdCAoaW4gdGVybXMgb2Ygc3BlZWQtdXBzL3Nsb3ctZG93bnMpLCBhbmQgaWYgdGhpcyBpcyBsaW5rZWQgdG8gaW5kaXZpZHVhbCBkaWZmZXJlbmNlcyBpbiBjYXVkYXRlIGFjdGl2aXR5IAogLSBvciBlcXVhbGx5LS13ZSBjb3VsZCBkbyB0aGlzIGF0IGEgdHJpYWwgbGV2ZWwuCgoKIyMgZmluZCBhIHdheSB0byBjb21wYXJlIGFjdGl2aXR5IGZyb20gdGhlIHN0YXJ0IGFuZCBlbmQuIElzIHRoZXJlIGEgc29ydCBvZiBoYWJpdHVhdGlvbiBlZmZlY3Q/CgpFYXN5IHdheSBmaXJzdC4gVGFrZSBqdXN0IENTIG9yIEZTLCB0aGVuIGRvIG9uZSBncmFwaCBwZXIgUk9JLCB1c2UgY29sb3Igd2l0aCB0aGUgb3JkZXIgb2YgdGhlIHRhc2suCgoKYGBge3J9Cgpyb2lfY29scyA8LSBjb2xuYW1lcyh0aW1lX3BvaW50c19jKVtncmVwbCgiaGFydmFyZG94Zm9yZCIsY29sbmFtZXModGltZV9wb2ludHNfYykpXQoKbmV4dF90cmlhbF9ncmFkaWVudCRocmZfdHA8LW5leHRfdHJpYWxfZ3JhZGllbnQkdHArNQoKdGltZV9wb2ludHNfbG9uZzwtdGltZV9wb2ludHNfYyAlPiUgcGl2b3RfbG9uZ2VyKGNvbHM9cm9pX2NvbHMsbmFtZXNfdG89IlJPSSIsdmFsdWVzX3RvPSJ2YWx1ZSIpCgp1bmlxdWVfcm9pczwtc29ydCh1bmlxdWUodGltZV9wb2ludHNfbG9uZyRST0kpKQp1bmlxdWVfcm9pc19vcmRlcmVkIDwtIHVuaXF1ZV9yb2lzW29yZGVyKHVuaXF1ZV9yb2lzICU+JSBzdHJfZXh0cmFjdCgiKD88PS57Myw1fVxcLikoW0EtWmEtel0qKSQiKSldCgp0aW1lX3BvaW50c19sb25nJFJPSTwtZmFjdG9yKHRpbWVfcG9pbnRzX2xvbmckUk9JLGxldmVscyA9IHVuaXF1ZV9yb2lzX29yZGVyZWQsIG9yZGVyZWQ9VFJVRSkKCgpmb3IgKGNfaSBpbiB1bmlxdWUodGltZV9wb2ludHNfbG9uZyRjb25kaXRpb24pKXsKICBjX3Bsb3QgPC0gZ2dwbG90KHRpbWVfcG9pbnRzX2xvbmcgJT4lIGZpbHRlcihjb25kaXRpb249PWNfaSksCiAgICAgICBhZXMoeD1vZmZzZXQsIHk9dmFsdWUsY29sb3I9dHJpYWxfbikpKwogIGZhY2V0X3dyYXAoflJPSSkrCiAgZ2VvbV90aWxlKGRhdGEgPSBuZXh0X3RyaWFsX2dyYWRpZW50LCBhZXMoeD1ocmZfdHAsYWxwaGE9cGN0X2luX3JhbmdlLHk9MCksZmlsbD0iIzc3ZmY3NyIsY29sb3I9InRyYW5zcGFyZW50IikrCiAgYW5ub3RhdGUoInRleHQiLHg9MTAseT0wLjIyLGxhYmVsPSJTdWJzZXF1ZW50IEdvIFRyaWFsXG5FeHBlY3RlZCBIUkYiLHNpemU9MikrCiAgZ2VvbV9saW5lKHNpemU9MSxhbHBoYT0wLjEpKwogICNzY2FsZV9jb2xvcl92aXJpZGlzX2Qob3B0aW9uID0gIkMiKSsKICAjY29vcmRfY2FydGVzaWFuKHlsaW09YygtMC4xLDAuMjUpKSsKICBnZ3Bsb3RfdGltZV9wb2ludHNfZXh0cmFzCiAgcHJpbnQoY19wbG90KQogIAp9CgpgYGAKClRoYXQgZG9lc24ndCB3b3JrLiBPSywgbGV0J3MgdHJ5IHB1dHRpbmcgdGhlIGFjdGl2aXR5IGludG8gYnVja2V0cyBvZiB0cmlhbHMgKGVhcmx5LCBtaWQsIGxhdGUpIHRoZW4gZ3JhcGggZm9yIHRob3NlIHRocmVlIGdyb3VwcwoKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCiNnZXQgdGhlIHJhbmdlIG9mIG9mZnNldHMKbWluX29mZnNldDwtcm91bmQobWluKHRpbWVfcG9pbnRzX2Mkb2Zmc2V0KSkKbWF4X29mZnNldCA8LSByb3VuZChtYXgodGltZV9wb2ludHNfYyRvZmZzZXQpKQoKc2VxX3NpemUgPC0gMC4xCm9mZnNldF9zaXplPC0xCm9mZnNldF90aW1lcyA8LSBzZXEobWluX29mZnNldCxtYXhfb2Zmc2V0LW1heChvZmZzZXRfc2l6ZSxzZXFfc2l6ZSksc2VxX3NpemUpCgpkdF9saXN0PC1saXN0KCkKdGltZV9wb2ludHNfYyR0cmlhbF9idWNrZXQ8LSIiCnRpbWVfcG9pbnRzX2MkdHJpYWxfYnVja2V0W3RpbWVfcG9pbnRzX2MkdHJpYWxfbjw9NDBdPC0iZWFybHkiCnRpbWVfcG9pbnRzX2MkdHJpYWxfYnVja2V0W3RpbWVfcG9pbnRzX2MkdHJpYWxfbiVpbiUgc2VxKDEzOS00MCwxMzksMildPC0ibWlkIgp0aW1lX3BvaW50c19jJHRyaWFsX2J1Y2tldFt0aW1lX3BvaW50c19jJHRyaWFsX24gJWluJSBzZXEoMjM5LTQwLDIzOSwyKV08LSJsYXRlIgoKCgoKZm9yIChjb25kIGluIHVuaXF1ZSh0aW1lX3BvaW50c19jJGNvbmRpdGlvbikpewogIHByaW50KGNvbmQpCiAgZm9yICh0Yl9pIGluIHVuaXF1ZSh0aW1lX3BvaW50c19jJHRyaWFsX2J1Y2tldCkpewogICAgICAKICAgIGZvciAob3RfaSBpbiBvZmZzZXRfdGltZXMpewogICAgICAKICAgICAgCiAgICAgIHRwX2F0X29mZnNldCA8LSB0aW1lX3BvaW50c19jICU+JSBmaWx0ZXIob2Zmc2V0Pj1vdF9pICYgb2Zmc2V0PChvdF9pK29mZnNldF9zaXplKSAmIGNvbmRpdGlvbj09Y29uZCAmIHRyaWFsX2J1Y2tldD09dGJfaSkKICAgICAgbnVtZXJpY19jb2xzIDwtIGNvbG5hbWVzKHRwX2F0X29mZnNldClbc2FwcGx5KHRwX2F0X29mZnNldCxjbGFzcyk9PSJudW1lcmljIl0KICAgICAgdHBfYXRfb2Zmc2V0X20gPC0gZGF0YS5mcmFtZSh0KGNvbE1lYW5zKHRwX2F0X29mZnNldFssbnVtZXJpY19jb2xzXSxuYS5ybT1UUlVFKSkpCiAgICAgIHRwX2F0X29mZnNldF9tJG9mZnNldF9zdGFydDwtb3RfaQogICAgICB0cF9hdF9vZmZzZXRfbSRjb25kaXRpb248LWNvbmQKICAgICAgdHBfYXRfb2Zmc2V0X20kdHJpYWxfYnVja2V0PC10Yl9pCiAgICAgIGR0X2xpc3Q8LWFwcGVuZChkdF9saXN0LGxpc3QodHBfYXRfb2Zmc2V0X20pKQogICAgICBpZigod2hpY2gob3RfaT09b2Zmc2V0X3RpbWVzKSUlMjApPT0wKXsKICAgICAgICBjYXQoIi4gIikKICAgICAgfQogICAgfQoKICB9Cgp9CgpkZl9vZmZzZXRfcnVuX2J1Y2tldHM8LWRhdGEuZnJhbWUoZGF0YS50YWJsZTo6cmJpbmRsaXN0KGR0X2xpc3QpKQoKCmBgYAoKCgpgYGB7cn0Kcm9pX2NvbHMgPC0gY29sbmFtZXMoZGZfb2Zmc2V0X3J1bl9idWNrZXRzKVtncmVwbCgiaGFydmFyZG94Zm9yZCIsY29sbmFtZXMoZGZfb2Zmc2V0X3J1bl9idWNrZXRzKSldCmRmX29mZnNldF9sb25nPC1kZl9vZmZzZXRfcnVuX2J1Y2tldHMgJT4lIHBpdm90X2xvbmdlcihjb2xzPXJvaV9jb2xzLG5hbWVzX3RvPSJST0kiLHZhbHVlc190bz0idmFsdWUiKQoKCmRmX29mZnNldF9sb25nIDwtIGRmX29mZnNldF9sb25nWyFpcy5uYW4oZGZfb2Zmc2V0X2xvbmckdHIpLCBdCmRmX29mZnNldF9sb25nJHRyaWFsX2J1Y2tldDwtZmFjdG9yKGRmX29mZnNldF9sb25nJHRyaWFsX2J1Y2tldCwgbGV2ZWxzPWMoImVhcmx5IiwibWlkIiwibGF0ZSIpKQpkZl9vZmZzZXRfbG9uZyRST0lfbGFiZWw8LWRmX29mZnNldF9sb25nJFJPSSAlPiUgYXMuY2hhcmFjdGVyICU+JSBzdHJpbmdyOjpzdHJfZXh0cmFjdF9hbGwocGF0dGVybiA9ICIoPzw9aGFydmFyZG94Zm9yZFxcLi57MCwzfWNvcnRpY2FsX3Byb2JfKSguKikiKSAlPiUgdW5saXN0CnVuaXF1ZV9yb2lzPC1zb3J0KHVuaXF1ZShkZl9vZmZzZXRfbG9uZyRST0lfbGFiZWwpKQp1bmlxdWVfcm9pc19vcmRlcmVkIDwtIHVuaXF1ZV9yb2lzW29yZGVyKHVuaXF1ZV9yb2lzICU+JSBzdHJfZXh0cmFjdCgiKD88PS57Myw1fVxcLikoW0EtWmEtel0qKSQiKSldCgpkZl9vZmZzZXRfbG9uZyRST0lfbGFiZWw8LWZhY3RvcihkZl9vZmZzZXRfbG9uZyRST0lfbGFiZWwsbGV2ZWxzID0gdW5pcXVlX3JvaXNfb3JkZXJlZCwgb3JkZXJlZD1UUlVFKQoKCmZvciAoY19pIGluIHVuaXF1ZShkZl9vZmZzZXRfbG9uZyRjb25kaXRpb24pKXsKICBjX3Bsb3QgPC0gZ2dwbG90KGRmX29mZnNldF9sb25nICAlPiUgZmlsdGVyKGNvbmRpdGlvbj09Y19pKSwjZmlsdGVyKGNvbmRpdGlvbj09Y19pKSwKICAgICAgICAgYWVzKHg9b2Zmc2V0X3N0YXJ0LCB5PXZhbHVlLGNvbG9yPXRyaWFsX2J1Y2tldCxncm91cD10cmlhbF9idWNrZXQpKSsKICAgIGZhY2V0X3dyYXAoflJPSV9sYWJlbCxuY29sID0gNCkrCiAgICAjZ2VvbV90aWxlKGRhdGEgPSBuZXh0X3RyaWFsX2dyYWRpZW50LCBhZXMoeD1ocmZfdHAsYWxwaGE9cGN0X2luX3JhbmdlLHk9MCksZmlsbD0iIzc3ZmY3NyIsY29sb3I9InRyYW5zcGFyZW50IikrCiAgICBhbm5vdGF0ZSgidGV4dCIseD0xMCx5PTAuMjIsbGFiZWw9IlN1YnNlcXVlbnQgR28gVHJpYWxcbkV4cGVjdGVkIEhSRiIsc2l6ZT0yKSsKICAgIGdlb21fbGluZShzaXplPTEsYWxwaGE9MC41KSsKICAgICNzY2FsZV9jb2xvcl92aXJpZGlzX2Qob3B0aW9uID0gIkMiKSsKICAgICNjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKC0wLjEsMC4yNSkpKwogICAgZ2dwbG90X3RpbWVfcG9pbnRzX2V4dHJhcysKICAgICAgbGFicygKICAgICAgdGl0bGU9IlN0b3AgU2lnbmFsIFRhc2sgQWN0aXZpdHkiLAogICAgICBzdWJ0aXRsZT1wYXN0ZTAoY19pKQogICAgKQogICAgCiAgICAKICAjZ2VvbV9wb2ludCgpKwogICNnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIixzcGFuPTIsbmEucm09VFJVRSkgKyAjaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dlb21fc21vb3RoLmh0bWwKICAjc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cz1jKC0yLjUsMi41KSkKICBwcmludChjX3Bsb3QpCn0KCmBgYAoKCgpgYGB7cn0KbmV4dF90cmlhbF9ncmFkaWVudCRocmZfdHA8LW5leHRfdHJpYWxfZ3JhZGllbnQkdHArNQoKZm9yIChjX2kgaW4gdW5pcXVlKGRmX29mZnNldF9sb25nJGNvbmRpdGlvbikpewogIGNfcGxvdCA8LSBnZ3Bsb3QoZGZfb2Zmc2V0X2xvbmcgJT4lIGZpbHRlcihjb25kaXRpb249PWNfaSAmIHRyaWFsX2J1Y2tldCE9IiIpLAogICAgICAgYWVzKHg9b2Zmc2V0X3N0YXJ0LCB5PXZhbHVlLGNvbG9yPVJPSV9sYWJlbCkpKwogIGZhY2V0X3dyYXAofnRyaWFsX2J1Y2tldCxucm93ID0gMSkrCiAgZ2VvbV90aWxlKGRhdGEgPSBuZXh0X3RyaWFsX2dyYWRpZW50LCBhZXMoeD1ocmZfdHAsYWxwaGE9cGN0X2luX3JhbmdlLHk9MCksZmlsbD0iIzc3ZmY3NyIsY29sb3I9InRyYW5zcGFyZW50IixzaG93LmxlZ2VuZD1GQUxTRSkrCiAgYW5ub3RhdGUoInRleHQiLHg9MTAseT0wLjIyLGxhYmVsPSJTdWJzZXF1ZW50IEdvIFRyaWFsXG5FeHBlY3RlZCBIUkYiLHNpemU9MikrCiAgZ2VvbV9saW5lKHNpemU9MSxhbHBoYT0wLjUpKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZChvcHRpb24gPSAiQyIpKwogIGNvb3JkX2NhcnRlc2lhbih5bGltPWMoLTAuNSwwLjUpKSsKICBnZ3Bsb3RfdGltZV9wb2ludHNfZXh0cmFzKwogICAgICBsYWJzKAogICAgICB0aXRsZT0iU3RvcCBTaWduYWwgVGFzayBBY3Rpdml0eSIsCiAgICAgIHN1YnRpdGxlPXBhc3RlMChjX2kpCiAgICApK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQogIAogIHByaW50KGNfcGxvdCkKfQoKYGBgCgpDYW4gd2UgY29udHJhc3QgQ1MgYW5kIEZTIGluIHRoZSB3YXkgd2UgZGlkIGFib3ZlPwoKCgpgYGB7cn0KCnRyaWFsX3Rwc19jb21wYXJlX2NvbmRpdGlvbnM8LWRmX29mZnNldF9sb25nICU+JSBwaXZvdF93aWRlcihpZF9jb2xzPWMoUk9JX2xhYmVsLG9mZnNldF9zdGFydCx0cmlhbF9idWNrZXQpLG5hbWVzX2Zyb20gPSAiY29uZGl0aW9uIix2YWx1ZXNfZnJvbT0idmFsdWUiKQp0cmlhbF90cHNfY29tcGFyZV9jb25kaXRpb25zJENTX21pbnVzX0ZTPC10cmlhbF90cHNfY29tcGFyZV9jb25kaXRpb25zJENvcnJlY3RTdG9wLXRyaWFsX3Rwc19jb21wYXJlX2NvbmRpdGlvbnMkRmFpbGVkU3RvcApgYGAKCgoKYGBge3J9CgogIGNfcGxvdCA8LSBnZ3Bsb3QodHJpYWxfdHBzX2NvbXBhcmVfY29uZGl0aW9ucyAlPiUgZmlsdGVyKHRyaWFsX2J1Y2tldCE9IiIpLAogICAgICAgYWVzKHg9b2Zmc2V0X3N0YXJ0LCB5PUNTX21pbnVzX0ZTLGNvbG9yPVJPSV9sYWJlbCkpKwogIGZhY2V0X3dyYXAofnRyaWFsX2J1Y2tldCxucm93ID0gMSkrCiAgZ2VvbV90aWxlKGRhdGEgPSBuZXh0X3RyaWFsX2dyYWRpZW50LCBhZXMoeD1ocmZfdHAsYWxwaGE9cGN0X2luX3JhbmdlLHk9MCksZmlsbD0iIzc3ZmY3NyIsY29sb3I9InRyYW5zcGFyZW50IixzaG93LmxlZ2VuZD1GQUxTRSkrCiAgYW5ub3RhdGUoInRleHQiLHg9MTAseT0wLjIyLGxhYmVsPSJTdWJzZXF1ZW50IEdvIFRyaWFsXG5FeHBlY3RlZCBIUkYiLHNpemU9MikrCiAgZ2VvbV9saW5lKHNpemU9MSxhbHBoYT0wLjUpKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZChvcHRpb24gPSAiQyIpKwogIGNvb3JkX2NhcnRlc2lhbih5bGltPWMoLTAuNSwwLjUpKSsKICBnZ3Bsb3RfdGltZV9wb2ludHNfZXh0cmFzKwogICAgICBsYWJzKAogICAgICB0aXRsZT0iU3RvcCBTaWduYWwgVGFzayBBY3Rpdml0eSIsCiAgICAgIHN1YnRpdGxlPXBhc3RlMChjX2kpLAogICAgICB5PSJDUy1GUyBCT0xEIGFjdGl2aXR5IGRpZmZlcmVuY2UiKSsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQpwcmludChjX3Bsb3QpCgojIAojIGdncGxvdCh0cmlhbF90cHNfY29tcGFyZV9jb25kaXRpb25zLAojICAgICAgICBhZXMoeD1vZmZzZXRfc3RhcnQsIHk9Q1NfbWludXNfRlMsY29sb3I9Uk9JKSkrCiMgICAjZ2VvbV90aWxlKGRhdGEgPSBuZXh0X3RyaWFsX2dyYWRpZW50LCBhZXMoeD10cCxhbHBoYT1wY3RfaW5fcmFuZ2UseT0wKSxmaWxsPSIjMDAwMDAwIixjb2xvcj0idHJhbnNwYXJlbnQiKSsKIyAgIGdlb21fdGlsZShkYXRhID0gbmV4dF90cmlhbF9ncmFkaWVudCwgYWVzKHg9aHJmX3RwLGFscGhhPXBjdF9pbl9yYW5nZSx5PTApLGZpbGw9IiM3N2ZmNzciLGNvbG9yPSJ0cmFuc3BhcmVudCIpKwojICAgI2Fubm90YXRlKCJ0ZXh0Iix4PTQseT0wLjI1LGxhYmVsPSJTdWJzZXF1ZW50IEdvIFRyaWFsXG5UaW1lIFJhbmdlIixzaXplPTIpKwojICAgYW5ub3RhdGUoInRleHQiLHg9MTAseT0wLjIyLGxhYmVsPSJTdWJzZXF1ZW50IEdvIFRyaWFsXG5FeHBlY3RlZCBIUkYiLHNpemU9MikrCiMgICBzY2FsZV9hbHBoYV9jb250aW51b3VzKG5hbWU9IlN1YnNlcXVlbnQgZ28gdHJpYWwgcmFuZ2UiLCByYW5nZT1jKDAsMC43KSxsYWJlbHM9c2NhbGVzOjpwZXJjZW50X2Zvcm1hdCgpKSsKIyAgIGdlb21fbGluZShzaXplPTEsYWxwaGE9MC41KSsKIyAgIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZChvcHRpb24gPSAiQyIpKwojICAgZ2dwbG90X3RpbWVfcG9pbnRzX2V4dHJhcysKIyAgIGNvb3JkX2NhcnRlc2lhbih5bGltPWMoLTAuMSwwLjI1KSkrCiMgICBsYWJzKHk9IkNTLUZTIEJPTEQgYWN0aXZpdHkgZGlmZmVyZW5jZSIpCiAgCmBgYAoKCgpUaGVyZSBhcmUgYSBkaWZmZXJlbnQgZGlzdHJpYnV0aW9uIG9mIHRoZSBudW1iZXIgb2YgdHJpYWxzIGZvciBlYWNoIGNvbmRpdGlvbiwgYnV0IG5vdCBpbiBhIHdheSB0aGF0IGNsZWFybHkgY291bGQgcHJvZHVjZSB0aGUgZGlzY3JlcGFuY3kgSSdtIG9ic2VydmluZyBhYm92ZS4KYGBge3J9CnRpbWVfcG9pbnRzX2MgJT4lIGZpbHRlcih0cmlhbF9idWNrZXQhPSIiKSAlPiUgZ3JvdXBfYnkoY29uZGl0aW9uLHRyaWFsX2J1Y2tldCkgJT4lIHN1bW1hcml6ZSh0cmlhbF9jb3VudD1uKCkpCmBgYAoKCg==